/*************************************************
*                                                *
*  EasyBMP Cross-Platform Windows Bitmap Library *
*                                                *
*  Author: Paul Macklin                          *
*   email: macklin01@users.sourceforge.net       *
* support: http://easybmp.sourceforge.net        *
*                                                *
*          file: EasyBMP.cpp                     *
*    date added: 03-31-2006                      *
* date modified: 12-01-2006                      *
*       version: 1.06                            *
*                                                *
*   License: BSD (revised/modified)              *
* Copyright: 2005-6 by the EasyBMP Project       *
*                                                *
* description: Actual source file                *
*                                                *
*************************************************/

#include "EasyBMP.h"

/* These functions are defined in EasyBMP.h */

static bool EBMP_EasyBMPwarnings = true;

void EBMP_SetEasyBMPwarningsOff( void )
{
    EBMP_EasyBMPwarnings = false;
}
void EBMP_SetEasyBMPwarningsOn( void )
{
    EBMP_EasyBMPwarnings = true;
}
bool EBMP_GetEasyBMPwarningState( void )
{
    return EBMP_EasyBMPwarnings;
}

/* These functions are defined in EasyBMP_DataStructures.h */

int EBMP_IntPow( int base, int exponent )
{
    int i;
    int output = 1;
    for ( i=0 ; i < exponent ; i++ )
    {
        output *= base;
    }
    return output;
}

BMFH_HANDLE BMFH_Construct()
{
	BMFH_HANDLE handle;

	handle = (BMFH_HANDLE)EBMP_Malloc(sizeof(BMFH));
	if(handle != NULL) {
		handle->bfType = 19778;
		handle->bfReserved1 = 0;
		handle->bfReserved2 = 0;
	}

	return handle;
}

void BMFH_Display(BMFH_HANDLE handle)
{
    printf("bfType: %d\n", (int) handle->bfType);
    printf("bfSize: %d\n", (int) handle->bfSize);
    printf("bfReserved1: %d\n", (int) handle->bfReserved1);
    printf("bfReserved2: %d\n", (int) handle->bfReserved2);
    printf("bfOffBits: %d\n\n", (int) handle->bfOffBits);
}

void BMFH_SwitchEndianess(BMFH_HANDLE handle)
{
    handle->bfType = EBMP_FlipWORD( handle->bfType );
    handle->bfSize = EBMP_FlipDWORD( handle->bfSize );
    handle->bfReserved1 = EBMP_FlipWORD( handle->bfReserved1 );
    handle->bfReserved2 = EBMP_FlipWORD( handle->bfReserved2 );
    handle->bfOffBits = EBMP_FlipDWORD( handle->bfOffBits );
}

void BMFH_Destruct(BMFH_HANDLE handle)
{
	EBMP_Free(handle);
}

BMIH::BMIH()
{
    biPlanes = 1;
    biCompression = 0;
    biXPelsPerMeter = DefaultXPelsPerMeter;
    biYPelsPerMeter = DefaultYPelsPerMeter;
    biClrUsed = 0;
    biClrImportant = 0;
}

void BMIH::BMIH_SwitchEndianess( void )
{
    biSize = EBMP_FlipDWORD( biSize );
    biWidth = EBMP_FlipDWORD( biWidth );
    biHeight = EBMP_FlipDWORD( biHeight );
    biPlanes = EBMP_FlipWORD( biPlanes );
    biBitCount = EBMP_FlipWORD( biBitCount );
    biCompression = EBMP_FlipDWORD( biCompression );
    biSizeImage = EBMP_FlipDWORD( biSizeImage );
    biXPelsPerMeter = EBMP_FlipDWORD( biXPelsPerMeter );
    biYPelsPerMeter = EBMP_FlipDWORD( biYPelsPerMeter );
    biClrUsed = EBMP_FlipDWORD( biClrUsed );
    biClrImportant = EBMP_FlipDWORD( biClrImportant );
    return;
}

void BMIH::BMIH_Display( void )
{
    printf("biSize: %d\n", (int) biSize);
    printf("biWidth: %d\n", (int) biWidth);
    printf("biHeight: %d\n", (int) biHeight);
    printf("biPlanes: %d\n", (int) biPlanes);
    printf("biBitCount: %d\n", (int) biBitCount);
    printf("biCompression: %d\n", (int) biCompression);
    printf("biSizeImage: %d\n", (int) biSizeImage);
    printf("biXPelsPerMeter: %d\n", (int) biXPelsPerMeter);
    printf("biYPelsPerMeter: %d\n", (int) biYPelsPerMeter);
    printf("biClrUsed: %d\n", (int) biClrUsed);
    printf("biClrImportant: %d\n\n", (int) biClrImportant);
}

/* These functions are defined in EasyBMP_BMP.h */

EBMP_RGBApixel BMP::BMP_GetPixel( int i, int j ) const
{
    bool Warn = false;
    if ( i >= Width )
    {
        i = Width-1;
        Warn = true;
    }
    if ( i < 0 )
    {
        i = 0;
        Warn = true;
    }
    if ( j >= Height )
    {
        j = Height-1;
        Warn = true;
    }
    if ( j < 0 )
    {
        j = 0;
        Warn = true;
    }
    if ( Warn && EBMP_EasyBMPwarnings )
    {
        printf("EasyBMP Warning: Attempted to access non-existent pixel;\n");
        printf("                 Truncating request to fit in the range [0,");
        printf("%d] x [0,%d].\n", (Width - 1), (Height - 1));
    }
    return Pixels[i][j];
}

bool BMP::BMP_SetPixel( int i, int j, EBMP_RGBApixel NewPixel )
{
    Pixels[i][j] = NewPixel;
    return true;
}


bool BMP::BMP_SetColor( int ColorNumber , EBMP_RGBApixel NewColor )
{
    if ( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Warning: Attempted to change color table for a BMP object\n");
            printf("                 that lacks a color table. Ignoring request.\n");
        }
        return false;
    }
    if ( !Colors )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Warning: Attempted to set a color, but the color table\n");
            printf("                 is not defined. Ignoring request.\n");
        }
        return false;
    }
    if ( ColorNumber >= BMP_TellNumberOfColors() )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Warning: Requested color number ");
            printf("%d is outside the allowed\n", ColorNumber);
            printf("                 range [0,%d", (BMP_TellNumberOfColors() - 1));
            printf("]. Ignoring request to set this color.\n");
        }
        return false;
    }
    Colors[ColorNumber] = NewColor;
    return true;
}

// RGBApixel BMP::GetColor( int ColorNumber ) const
EBMP_RGBApixel BMP::BMP_GetColor( int ColorNumber )
{
    EBMP_RGBApixel Output;
    Output.Red   = 255;
    Output.Green = 255;
    Output.Blue  = 255;
    Output.Alpha = 0;

    if ( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Warning: Attempted to access color table for a BMP object\n");
            printf("                 that lacks a color table. Ignoring request.\n");
        }
        return Output;
    }
    if ( !Colors )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Warning: Requested a color, but the color table\n");
            printf("                 is not defined. Ignoring request.\n");
        }
        return Output;
    }
    if ( ColorNumber >= BMP_TellNumberOfColors() )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Warning: Requested color number ");
            printf("%d is outside the allowed\n", ColorNumber);
            printf("                 range [0,%d", (BMP_TellNumberOfColors() - 1));
            printf("]. Ignoring request to get this color.\n");
        }
        return Output;
    }
    Output = Colors[ColorNumber];
    return Output;
}

BMP::BMP()
{
    Width = 1;
    Height = 1;
    BitDepth = 24;
    Pixels = (EBMP_RGBApixel**)EBMP_Malloc(sizeof(EBMP_RGBApixel) * Width);
    Pixels[0] = (EBMP_RGBApixel*)EBMP_Malloc(sizeof(EBMP_RGBApixel) * Height);
    Colors = NULL;

    XPelsPerMeter = 0;
    YPelsPerMeter = 0;

    MetaData1 = NULL;
    SizeOfMetaData1 = 0;
    MetaData2 = NULL;
    SizeOfMetaData2 = 0;
}

// BMP::BMP( const BMP& Input )
BMP::BMP( BMP& Input )
{
// first, make the image empty.

    Width = 1;
    Height = 1;
    BitDepth = 24;
    Pixels = (EBMP_RGBApixel**)EBMP_Malloc(sizeof(EBMP_RGBApixel) * Width);
    Pixels[0] = (EBMP_RGBApixel*)EBMP_Malloc(sizeof(EBMP_RGBApixel) * Height);
    Colors = NULL;
    XPelsPerMeter = 0;
    YPelsPerMeter = 0;

    MetaData1 = NULL;
    SizeOfMetaData1 = 0;
    MetaData2 = NULL;
    SizeOfMetaData2 = 0;

// now, set the correct bit depth

    BMP_SetBitDepth( Input.BMP_TellBitDepth() );

// set the correct pixel size

    BMP_SetSize( Input.BMP_TellWidth() , Input.BMP_TellHeight() );

// set the DPI information from Input

    BMP_SetDPI( Input.BMP_TellHorizontalDPI() , Input.BMP_TellVerticalDPI() );

// if there is a color table, get all the colors

    if ( BitDepth == 1 || BitDepth == 4 ||
            BitDepth == 8 )
    {
        for ( int k=0 ; k < BMP_TellNumberOfColors() ; k++ )
        {
            BMP_SetColor( k, Input.BMP_GetColor( k ));
        }
    }

// get all the pixels

    for ( int j=0; j < Height ; j++ )
    {
        for ( int i=0; i < Width ; i++ )
        {
            Pixels[i][j] = *Input(i,j);
//   Pixels[i][j] = Input.GetPixel(i,j); // *Input(i,j);
        }
    }
}

BMP::~BMP()
{
    int i;
    for (i=0; i<Width; i++)
    {
        EBMP_Free(Pixels[i]);
    }
    EBMP_Free(Pixels);
    if ( Colors )
    {
        EBMP_Free(Colors);
    }

    if ( MetaData1 )
    {
        EBMP_Free(MetaData1);
    }
    if ( MetaData2 )
    {
        EBMP_Free(MetaData2);
    }
}

EBMP_RGBApixel* BMP::operator()(int i, int j)
{
    bool Warn = false;
    if ( i >= Width )
    {
        i = Width-1;
        Warn = true;
    }
    if ( i < 0 )
    {
        i = 0;
        Warn = true;
    }
    if ( j >= Height )
    {
        j = Height-1;
        Warn = true;
    }
    if ( j < 0 )
    {
        j = 0;
        Warn = true;
    }
    if ( Warn && EBMP_EasyBMPwarnings )
    {
        printf("EasyBMP Warning: Attempted to access non-existent pixel;\n");
        printf("                 Truncating request to fit in the range [0,");
        printf("%d] x [0,%d].", (Width - 1), (Height - 1));
    }
    return &(Pixels[i][j]);
}

// int BMP::TellBitDepth( void ) const
int BMP::BMP_TellBitDepth( void )
{
    return BitDepth;
}

// int BMP::TellHeight( void ) const
int BMP::BMP_TellHeight( void )
{
    return Height;
}

// int BMP::TellWidth( void ) const
int BMP::BMP_TellWidth( void )
{
    return Width;
}

// int BMP::TellNumberOfColors( void ) const
int BMP::BMP_TellNumberOfColors( void )
{
    int output = EBMP_IntPow( 2, BitDepth );
    if ( BitDepth == 32 )
    {
        output = EBMP_IntPow( 2, 24 );
    }
    return output;
}

bool BMP::BMP_SetBitDepth( int NewDepth )
{
    if ( NewDepth != 1 && NewDepth != 4 &&
            NewDepth != 8 && NewDepth != 16 &&
            NewDepth != 24 && NewDepth != 32 )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Warning: User attempted to set unsupported bit depth ");
            printf("%d.\n", NewDepth);
            printf("                 Bit depth remains unchanged at ");
            printf("%d.\n", BitDepth);
        }
        return false;
    }

    BitDepth = NewDepth;
    if ( Colors )
    {
        EBMP_Free(Colors);
    }
    int NumberOfColors = EBMP_IntPow( 2, BitDepth );
    if ( BitDepth == 1 || BitDepth == 4 || BitDepth == 8 )
    {
        Colors = (EBMP_RGBApixel*)EBMP_Malloc(sizeof(EBMP_RGBApixel) * NumberOfColors);
    }
    else
    {
        Colors = NULL;
    }
    if ( BitDepth == 1 || BitDepth == 4 || BitDepth == 8 )
    {
        BMP_CreateStandardColorTable();
    }

    return true;
}

bool BMP::BMP_SetSize(int NewWidth , int NewHeight )
{
    if ( NewWidth <= 0 || NewHeight <= 0 )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Warning: User attempted to set a non-positive width or height.\n");
            printf("                 Size remains unchanged at ");
            printf("%d x %d.\n", Width, Height);
        }
        return false;
    }

    int i,j;

    for (i=0; i<Width; i++)
    {
        EBMP_Free(Pixels[i]);
    }
    EBMP_Free(Pixels);

    Width = NewWidth;
    Height = NewHeight;
    Pixels = (EBMP_RGBApixel**)EBMP_Malloc(sizeof(EBMP_RGBApixel) * Width);

    for (i=0; i<Width; i++)
    {
        Pixels[i] = (EBMP_RGBApixel*)EBMP_Malloc(sizeof(EBMP_RGBApixel) * Height);
    }

    for ( i=0 ; i < Width ; i++)
    {
        for ( j=0 ; j < Height ; j++ )
        {
            Pixels[i][j].Red = 255;
            Pixels[i][j].Green = 255;
            Pixels[i][j].Blue = 255;
            Pixels[i][j].Alpha = 0;
        }
    }

    return true;
}

bool BMP::BMP_WriteToFile( const char* FileName )
{
    if ( !EBMP_EasyBMPcheckDataSize() )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Error: Data types are wrong size!\n");
            printf("               You may need to mess with EasyBMP_DataTypes.h\n");
            printf("               to fix these errors, and then recompile.\n");
            printf("               All 32-bit and 64-bit machines should be\n");
            printf("               supported, however.\n\n");
        }
        return false;
    }

    FILE* fp = fopen( FileName, "wb" );
    if ( fp == NULL )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Error: Cannot open file ");
            printf("%s for output.\n", FileName);
        }
        fclose( fp );
        return false;
    }

// some preliminaries

    double dBytesPerPixel = ( (double) BitDepth ) / 8.0;
    double dBytesPerRow = dBytesPerPixel * (Width+0.0);
    dBytesPerRow = ceil(dBytesPerRow);

    int BytePaddingPerRow = 4 - ( (int) (dBytesPerRow) )% 4;
    if ( BytePaddingPerRow == 4 )
    {
        BytePaddingPerRow = 0;
    }

    double dActualBytesPerRow = dBytesPerRow + BytePaddingPerRow;

    double dTotalPixelBytes = Height * dActualBytesPerRow;

    double dPaletteSize = 0;
    if ( BitDepth == 1 || BitDepth == 4 || BitDepth == 8 )
    {
        dPaletteSize = EBMP_IntPow(2,BitDepth)*4.0;
    }

// leave some room for 16-bit masks
    if ( BitDepth == 16 )
    {
        dPaletteSize = 3*4;
    }

    double dTotalFileSize = 14 + 40 + dPaletteSize + dTotalPixelBytes;

// write the file header
    {
	    BMFH_HANDLE bmfh = BMFH_Construct();
	    bmfh->bfSize = (ebmpDWORD) dTotalFileSize;
	    bmfh->bfReserved1 = 0;
	    bmfh->bfReserved2 = 0;
	    bmfh->bfOffBits = (ebmpDWORD) (14+40+dPaletteSize);

	    if ( EBMP_IsBigEndian() )
	    {
		BMFH_SwitchEndianess(bmfh);
	    }

	    fwrite( (char*) &(bmfh->bfType) , sizeof(ebmpWORD) , 1 , fp );
	    fwrite( (char*) &(bmfh->bfSize) , sizeof(ebmpDWORD) , 1 , fp );
	    fwrite( (char*) &(bmfh->bfReserved1) , sizeof(ebmpWORD) , 1 , fp );
	    fwrite( (char*) &(bmfh->bfReserved2) , sizeof(ebmpWORD) , 1 , fp );
	    fwrite( (char*) &(bmfh->bfOffBits) , sizeof(ebmpDWORD) , 1 , fp );
	    BMFH_Destruct(bmfh);
    }

// write the info header

    BMIH bmih;
    bmih.biSize = 40;
    bmih.biWidth = Width;
    bmih.biHeight = Height;
    bmih.biPlanes = 1;
    bmih.biBitCount = BitDepth;
    bmih.biCompression = 0;
    bmih.biSizeImage = (ebmpDWORD) dTotalPixelBytes;
    if ( XPelsPerMeter )
    {
        bmih.biXPelsPerMeter = XPelsPerMeter;
    }
    else
    {
        bmih.biXPelsPerMeter = DefaultXPelsPerMeter;
    }
    if ( YPelsPerMeter )
    {
        bmih.biYPelsPerMeter = YPelsPerMeter;
    }
    else
    {
        bmih.biYPelsPerMeter = DefaultYPelsPerMeter;
    }

    bmih.biClrUsed = 0;
    bmih.biClrImportant = 0;

// indicates that we'll be using bit fields for 16-bit files
    if ( BitDepth == 16 )
    {
        bmih.biCompression = 3;
    }

    if ( EBMP_IsBigEndian() )
    {
        bmih.BMIH_SwitchEndianess();
    }

    fwrite( (char*) &(bmih.biSize) , sizeof(ebmpDWORD) , 1 , fp );
    fwrite( (char*) &(bmih.biWidth) , sizeof(ebmpDWORD) , 1 , fp );
    fwrite( (char*) &(bmih.biHeight) , sizeof(ebmpDWORD) , 1 , fp );
    fwrite( (char*) &(bmih.biPlanes) , sizeof(ebmpWORD) , 1 , fp );
    fwrite( (char*) &(bmih.biBitCount) , sizeof(ebmpWORD) , 1 , fp );
    fwrite( (char*) &(bmih.biCompression) , sizeof(ebmpDWORD) , 1 , fp );
    fwrite( (char*) &(bmih.biSizeImage) , sizeof(ebmpDWORD) , 1 , fp );
    fwrite( (char*) &(bmih.biXPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp );
    fwrite( (char*) &(bmih.biYPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp );
    fwrite( (char*) &(bmih.biClrUsed) , sizeof(ebmpDWORD) , 1 , fp);
    fwrite( (char*) &(bmih.biClrImportant) , sizeof(ebmpDWORD) , 1 , fp);

// write the palette
    if ( BitDepth == 1 || BitDepth == 4 || BitDepth == 8 )
    {
        int NumberOfColors = EBMP_IntPow(2,BitDepth);

        // if there is no palette, create one
        if ( !Colors )
        {
            if ( !Colors )
            {
                Colors = (EBMP_RGBApixel*)EBMP_Malloc(sizeof(EBMP_RGBApixel) * NumberOfColors);
            }
            BMP_CreateStandardColorTable();
        }

        int n;
        for ( n=0 ; n < NumberOfColors ; n++ )
        {
            fwrite( (char*) &(Colors[n]) , 4 , 1 , fp );
        }
    }

// write the pixels
    int i,j;
    if ( BitDepth != 16 )
    {
        ebmpBYTE* Buffer;
        int BufferSize = (int) ( (Width*BitDepth)/8.0 );
        while ( 8*BufferSize < Width*BitDepth )
        {
            BufferSize++;
        }
        while ( BufferSize % 4 )
        {
            BufferSize++;
        }

        Buffer = (ebmpBYTE*)EBMP_Malloc(sizeof(ebmpBYTE) * BufferSize);
        for ( j=0 ; j < BufferSize; j++ )
        {
            Buffer[j] = 0;
        }

        j=Height-1;

        while ( j > -1 )
        {
            bool Success = false;
            if ( BitDepth == 32 )
            {
                Success = BMP_Write32bitRow( Buffer, BufferSize, j );
            }
            if ( BitDepth == 24 )
            {
                Success = BMP_Write24bitRow( Buffer, BufferSize, j );
            }
            if ( BitDepth == 8  )
            {
                Success = BMP_Write8bitRow( Buffer, BufferSize, j );
            }
            if ( BitDepth == 4  )
            {
                Success = BMP_Write4bitRow( Buffer, BufferSize, j );
            }
            if ( BitDepth == 1  )
            {
                Success = BMP_Write1bitRow( Buffer, BufferSize, j );
            }
            if ( Success )
            {
                int BytesWritten = (int) fwrite( (char*) Buffer, 1, BufferSize, fp );
                if ( BytesWritten != BufferSize )
                {
                    Success = false;
                }
            }
            if ( !Success )
            {
                if ( EBMP_EasyBMPwarnings )
                {
                    printf("EasyBMP Error: Could not write proper amount of data.\n");
                }
                j = -1;
            }
            j--;
        }

        EBMP_Free(Buffer);
    }

    if ( BitDepth == 16 )
    {
        // write the bit masks

        ebmpWORD BlueMask = 31;    // bits 12-16
        ebmpWORD GreenMask = 2016; // bits 6-11
        ebmpWORD RedMask = 63488;  // bits 1-5
        ebmpWORD ZeroWORD;

        if ( EBMP_IsBigEndian() )
        {
            RedMask = EBMP_FlipWORD( RedMask );
        }
        fwrite( (char*) &RedMask , 2 , 1 , fp );
        fwrite( (char*) &ZeroWORD , 2 , 1 , fp );

        if ( EBMP_IsBigEndian() )
        {
            GreenMask = EBMP_FlipWORD( GreenMask );
        }
        fwrite( (char*) &GreenMask , 2 , 1 , fp );
        fwrite( (char*) &ZeroWORD , 2 , 1 , fp );

        if ( EBMP_IsBigEndian() )
        {
            BlueMask = EBMP_FlipWORD( BlueMask );
        }
        fwrite( (char*) &BlueMask , 2 , 1 , fp );
        fwrite( (char*) &ZeroWORD , 2 , 1 , fp );

        int DataBytes = Width*2;
        int PaddingBytes = ( 4 - DataBytes % 4 ) % 4;

        // write the actual pixels

        for ( j=Height-1 ; j >= 0 ; j-- )
        {
            // write all row pixel data
            i=0;
            int WriteNumber = 0;
            while ( WriteNumber < DataBytes )
            {
                ebmpWORD TempWORD;

                ebmpWORD RedWORD = (ebmpWORD) ((Pixels[i][j]).Red / 8);
                ebmpWORD GreenWORD = (ebmpWORD) ((Pixels[i][j]).Green / 4);
                ebmpWORD BlueWORD = (ebmpWORD) ((Pixels[i][j]).Blue / 8);

                TempWORD = (RedWORD<<11) + (GreenWORD<<5) + BlueWORD;
                if ( EBMP_IsBigEndian() )
                {
                    TempWORD = EBMP_FlipWORD( TempWORD );
                }

                fwrite( (char*) &TempWORD , 2, 1, fp);
                WriteNumber += 2;
                i++;
            }
            // write any necessary row padding
            WriteNumber = 0;
            while ( WriteNumber < PaddingBytes )
            {
                ebmpBYTE TempBYTE;
                fwrite( (char*) &TempBYTE , 1, 1, fp);
                WriteNumber++;
            }
        }

    }

    fclose(fp);
    return true;
}

bool BMP::BMP_ReadFromFile( const char* FileName )
{
    if ( !EBMP_EasyBMPcheckDataSize() )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Error: Data types are wrong size!\n");
            printf("               You may need to mess with EasyBMP_DataTypes.h\n");
            printf("               to fix these errors, and then recompile.\n");
            printf("               All 32-bit and 64-bit machines should be\n");
            printf("               supported, however.\n\n");
        }
        return false;
    }

    FILE* fp = fopen( FileName, "rb" );
    if ( fp == NULL )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Error: Cannot open file ");
            printf("%s for input.\n", FileName);
        }
        BMP_SetBitDepth(1);
        BMP_SetSize(1,1);
        return false;
    }

// read the file header

    BMFH_HANDLE bmfh = BMFH_Construct();
    bool NotCorrupted = true;

    NotCorrupted &= EBMP_SafeFread( (char*) &(bmfh->bfType) , sizeof(ebmpWORD), 1, fp);

    bool IsBitmap = false;

    if ( EBMP_IsBigEndian() && bmfh->bfType == 16973 )
    {
        IsBitmap = true;
    }
    if ( !EBMP_IsBigEndian() && bmfh->bfType == 19778 )
    {
        IsBitmap = true;
    }

    if ( !IsBitmap )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Error: %s", FileName);
            printf(" is not a Windows BMP file!\n");
        }
		BMFH_Destruct(bmfh);
		fclose(fp);
		return false;
    }

    NotCorrupted &= EBMP_SafeFread( (char*) &(bmfh->bfSize) , sizeof(ebmpDWORD) , 1, fp);
    NotCorrupted &= EBMP_SafeFread( (char*) &(bmfh->bfReserved1) , sizeof(ebmpWORD) , 1, fp);
    NotCorrupted &= EBMP_SafeFread( (char*) &(bmfh->bfReserved2) , sizeof(ebmpWORD) , 1, fp);
    NotCorrupted &= EBMP_SafeFread( (char*) &(bmfh->bfOffBits) , sizeof(ebmpDWORD) , 1 , fp);

    if ( EBMP_IsBigEndian() )
    {
        BMFH_SwitchEndianess(bmfh);
    }

// read the info header

    BMIH bmih;

    NotCorrupted &= EBMP_SafeFread( (char*) &(bmih.biSize) , sizeof(ebmpDWORD) , 1 , fp);
    NotCorrupted &= EBMP_SafeFread( (char*) &(bmih.biWidth) , sizeof(ebmpDWORD) , 1 , fp);
    NotCorrupted &= EBMP_SafeFread( (char*) &(bmih.biHeight) , sizeof(ebmpDWORD) , 1 , fp);
    NotCorrupted &= EBMP_SafeFread( (char*) &(bmih.biPlanes) , sizeof(ebmpWORD) , 1, fp);
    NotCorrupted &= EBMP_SafeFread( (char*) &(bmih.biBitCount) , sizeof(ebmpWORD) , 1, fp);

    NotCorrupted &= EBMP_SafeFread( (char*) &(bmih.biCompression) , sizeof(ebmpDWORD) , 1 , fp);
    NotCorrupted &= EBMP_SafeFread( (char*) &(bmih.biSizeImage) , sizeof(ebmpDWORD) , 1 , fp);
    NotCorrupted &= EBMP_SafeFread( (char*) &(bmih.biXPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp);
    NotCorrupted &= EBMP_SafeFread( (char*) &(bmih.biYPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp);
    NotCorrupted &= EBMP_SafeFread( (char*) &(bmih.biClrUsed) , sizeof(ebmpDWORD) , 1 , fp);
    NotCorrupted &= EBMP_SafeFread( (char*) &(bmih.biClrImportant) , sizeof(ebmpDWORD) , 1 , fp);

    if ( EBMP_IsBigEndian() )
    {
        bmih.BMIH_SwitchEndianess();
    }

// a safety catch: if any of the header information didn't read properly, abort
// future idea: check to see if at least most is self-consistent

    if ( !NotCorrupted )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Error: %s", FileName);
            printf(" is obviously corrupted.\n");
        }
        BMP_SetSize(1,1);
        BMP_SetBitDepth(1);
		BMFH_Destruct(bmfh);
		fclose(fp);
		return false;
    }

    XPelsPerMeter = bmih.biXPelsPerMeter;
    YPelsPerMeter = bmih.biYPelsPerMeter;

// if bmih.biCompression 1 or 2, then the file is RLE compressed

    if ( bmih.biCompression == 1 || bmih.biCompression == 2 )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Error: %s is (RLE) compressed.\n", FileName);
            printf("               EasyBMP does not support compression.\n");
        }
        BMP_SetSize(1,1);
        BMP_SetBitDepth(1);
		BMFH_Destruct(bmfh);
		fclose(fp);
		return false;
    }

// if bmih.biCompression > 3, then something strange is going on
// it's probably an OS2 bitmap file.

    if ( bmih.biCompression > 3 )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Error: %s is in an unsupported format.\n", FileName);
            printf("               (bmih.biCompression = ");
            printf("%u)\n", bmih.biCompression);
            printf("               The file is probably an old OS2 bitmap or corrupted.\n");
        }
        BMP_SetSize(1,1);
        BMP_SetBitDepth(1);
		BMFH_Destruct(bmfh);
		fclose(fp);
		return false;
    }

    if ( bmih.biCompression == 3 && bmih.biBitCount != 16 )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Error: %s", FileName);
            printf(" uses bit fields and is not a\n");
            printf("               16-bit file. This is not supported.\n");
        }
        BMP_SetSize(1,1);
        BMP_SetBitDepth(1);
		BMFH_Destruct(bmfh);
		fclose(fp);
		return false;
    }

// set the bit depth

    int TempBitDepth = (int) bmih.biBitCount;
    if (    TempBitDepth != 1  && TempBitDepth != 4
            && TempBitDepth != 8  && TempBitDepth != 16
            && TempBitDepth != 24 && TempBitDepth != 32 )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Error: %s has unrecognized bit depth.\n", FileName);
        }
        BMP_SetSize(1,1);
        BMP_SetBitDepth(1);
		BMFH_Destruct(bmfh);
		fclose(fp);
		return false;
    }
    BMP_SetBitDepth( (int) bmih.biBitCount );

// set the size

    if ( (int) bmih.biWidth <= 0 || (int) bmih.biHeight <= 0 )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Error: %s", FileName);
            printf(" has a non-positive width or height.\n");
        }
        BMP_SetSize(1,1);
        BMP_SetBitDepth(1);
		BMFH_Destruct(bmfh);
		fclose(fp);
		return false;
    }
    BMP_SetSize( (int) bmih.biWidth , (int) bmih.biHeight );

// some preliminaries

    double dBytesPerPixel = ( (double) BitDepth ) / 8.0;
    double dBytesPerRow = dBytesPerPixel * (Width+0.0);
    dBytesPerRow = ceil(dBytesPerRow);

    int BytePaddingPerRow = 4 - ( (int) (dBytesPerRow) )% 4;
    if ( BytePaddingPerRow == 4 )
    {
        BytePaddingPerRow = 0;
    }

// if < 16 bits, read the palette

    if ( BitDepth < 16 )
    {
        // determine the number of colors specified in the
        // color table

        int NumberOfColorsToRead = ((int) bmfh->bfOffBits - 54 )/4;
        if ( NumberOfColorsToRead > EBMP_IntPow(2,BitDepth) )
        {
            NumberOfColorsToRead = EBMP_IntPow(2,BitDepth);
        }

        if ( NumberOfColorsToRead < BMP_TellNumberOfColors() )
        {
            if ( EBMP_EasyBMPwarnings )
            {
                printf("EasyBMP Warning: file %s has an underspecified\n", FileName);
                printf("                 color table. The table will be padded with extra\n");
                printf("                 white (255,255,255,0) entries.\n");
            }
        }

        int n;
        for ( n=0; n < NumberOfColorsToRead ; n++ )
        {
            EBMP_SafeFread( (char*) &(Colors[n]) , 4 , 1 , fp);
        }
        for ( n=NumberOfColorsToRead ; n < BMP_TellNumberOfColors() ; n++ )
        {
            EBMP_RGBApixel WHITE;
            WHITE.Red = 255;
            WHITE.Green = 255;
            WHITE.Blue = 255;
            WHITE.Alpha = 0;
            BMP_SetColor( n , WHITE );
        }


    }

// skip blank data if bfOffBits so indicates

    int BytesToSkip = bmfh->bfOffBits - 54;;
    if ( BitDepth < 16 )
    {
        BytesToSkip -= 4*EBMP_IntPow(2,BitDepth);
    }
    if ( BitDepth == 16 && bmih.biCompression == 3 )
    {
        BytesToSkip -= 3*4;
    }
    if ( BytesToSkip < 0 )
    {
        BytesToSkip = 0;
    }
    if ( BytesToSkip > 0 && BitDepth != 16 )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Warning: Extra meta data detected in file %s\n", FileName);
            printf("                 Data will be skipped.\n");
        }
        ebmpBYTE* TempSkipBYTE;
        TempSkipBYTE = (ebmpBYTE*)EBMP_Malloc(sizeof(ebmpBYTE) * BytesToSkip);
        EBMP_SafeFread( (char*) TempSkipBYTE , BytesToSkip , 1 , fp);
        EBMP_Free(TempSkipBYTE);
    }

// This code reads 1, 4, 8, 24, and 32-bpp files
// with a more-efficient buffered technique.

    int i,j;
    if ( BitDepth != 16 )
    {
        int BufferSize = (int) ( (Width*BitDepth) / 8.0 );
        while ( 8*BufferSize < Width*BitDepth )
        {
            BufferSize++;
        }
        while ( BufferSize % 4 )
        {
            BufferSize++;
        }
        ebmpBYTE* Buffer;
        Buffer = (ebmpBYTE*)EBMP_Malloc(sizeof(ebmpBYTE) * BufferSize);
        j= Height-1;
        while ( j > -1 )
        {
            int BytesRead = (int) fread( (char*) Buffer, 1, BufferSize, fp );
            if ( BytesRead < BufferSize )
            {
                j = -1;
                if ( EBMP_EasyBMPwarnings )
                {
                    printf("EasyBMP Error: Could not read proper amount of data.\n");
                }
            }
            else
            {
                bool Success = false;
                if ( BitDepth == 1  )
                {
                    Success = BMP_Read1bitRow(  Buffer, BufferSize, j );
                }
                if ( BitDepth == 4  )
                {
                    Success = BMP_Read4bitRow(  Buffer, BufferSize, j );
                }
                if ( BitDepth == 8  )
                {
                    Success = BMP_Read8bitRow(  Buffer, BufferSize, j );
                }
                if ( BitDepth == 24 )
                {
                    Success = BMP_Read24bitRow( Buffer, BufferSize, j );
                }
                if ( BitDepth == 32 )
                {
                    Success = BMP_Read32bitRow( Buffer, BufferSize, j );
                }
                if ( !Success )
                {
                    if ( EBMP_EasyBMPwarnings )
                    {
                        printf("EasyBMP Error: Could not read enough pixel data!\n");
                    }
                    j = -1;
                }
            }
            j--;
        }
        EBMP_Free(Buffer);
    }

    if ( BitDepth == 16 )
    {
        int DataBytes = Width*2;
        int PaddingBytes = ( 4 - DataBytes % 4 ) % 4;

        // set the default mask

        ebmpWORD BlueMask = 31; // bits 12-16
        ebmpWORD GreenMask = 992; // bits 7-11
        ebmpWORD RedMask = 31744; // bits 2-6

        // read the bit fields, if necessary, to
        // override the default 5-5-5 mask

        if ( bmih.biCompression != 0 )
        {
            // read the three bit masks

            ebmpWORD TempMaskWORD;

            EBMP_SafeFread( (char*) &RedMask , 2 , 1 , fp );
            if ( EBMP_IsBigEndian() )
            {
                RedMask = EBMP_FlipWORD(RedMask);
            }
            EBMP_SafeFread( (char*) &TempMaskWORD , 2, 1, fp );

            EBMP_SafeFread( (char*) &GreenMask , 2 , 1 , fp );
            if ( EBMP_IsBigEndian() )
            {
                GreenMask = EBMP_FlipWORD(GreenMask);
            }
            EBMP_SafeFread( (char*) &TempMaskWORD , 2, 1, fp );

            EBMP_SafeFread( (char*) &BlueMask , 2 , 1 , fp );
            if ( EBMP_IsBigEndian() )
            {
                BlueMask = EBMP_FlipWORD(BlueMask);
            }
            EBMP_SafeFread( (char*) &TempMaskWORD , 2, 1, fp );
        }

        // read and skip any meta data

        if ( BytesToSkip > 0 )
        {
            if ( EBMP_EasyBMPwarnings )
            {
                printf("EasyBMP Warning: Extra meta data detected in file ");
                printf("%s\n", FileName);
                printf("                 Data will be skipped.\n");
            }
            ebmpBYTE* TempSkipBYTE;
            TempSkipBYTE = (ebmpBYTE*)EBMP_Malloc(sizeof(ebmpBYTE) * BytesToSkip);
            EBMP_SafeFread( (char*) TempSkipBYTE , BytesToSkip , 1 , fp);
            EBMP_Free(TempSkipBYTE);
        }

        // determine the red, green and blue shifts

        int GreenShift = 0;
        ebmpWORD TempShiftWORD = GreenMask;
        while ( TempShiftWORD > 31 )
        {
            TempShiftWORD = TempShiftWORD>>1;
            GreenShift++;
        }
        int BlueShift = 0;
        TempShiftWORD = BlueMask;
        while ( TempShiftWORD > 31 )
        {
            TempShiftWORD = TempShiftWORD>>1;
            BlueShift++;
        }
        int RedShift = 0;
        TempShiftWORD = RedMask;
        while ( TempShiftWORD > 31 )
        {
            TempShiftWORD = TempShiftWORD>>1;
            RedShift++;
        }

        // read the actual pixels

        for ( j=Height-1 ; j >= 0 ; j-- )
        {
            i=0;
            int ReadNumber = 0;
            while ( ReadNumber < DataBytes )
            {
                ebmpWORD TempWORD;
                EBMP_SafeFread( (char*) &TempWORD , 2 , 1 , fp );
                if ( EBMP_IsBigEndian() )
                {
                    TempWORD = EBMP_FlipWORD(TempWORD);
                }
                ReadNumber += 2;

                ebmpWORD Red = RedMask & TempWORD;
                ebmpWORD Green = GreenMask & TempWORD;
                ebmpWORD Blue = BlueMask & TempWORD;

                ebmpBYTE BlueBYTE = (ebmpBYTE) 8*(Blue>>BlueShift);
                ebmpBYTE GreenBYTE = (ebmpBYTE) 8*(Green>>GreenShift);
                ebmpBYTE RedBYTE = (ebmpBYTE) 8*(Red>>RedShift);

                (Pixels[i][j]).Red = RedBYTE;
                (Pixels[i][j]).Green = GreenBYTE;
                (Pixels[i][j]).Blue = BlueBYTE;

                i++;
            }
            ReadNumber = 0;
            while ( ReadNumber < PaddingBytes )
            {
                ebmpBYTE TempBYTE;
                EBMP_SafeFread( (char*) &TempBYTE , 1, 1, fp);
                ReadNumber++;
            }
        }

    }

	BMFH_Destruct(bmfh);
    fclose(fp);
    return true;
}

bool BMP::BMP_CreateStandardColorTable( void )
{
    if ( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Warning: Attempted to create color table at a bit\n");
            printf("                 depth that does not require a color table.\n");
            printf("                 Ignoring request.\n");
        }
        return false;
    }

    if ( BitDepth == 1 )
    {
        int i;
        for ( i=0 ; i < 2 ; i++ )
        {
            Colors[i].Red = i*255;
            Colors[i].Green = i*255;
            Colors[i].Blue = i*255;
            Colors[i].Alpha = 0;
        }
        return true;
    }

    if ( BitDepth == 4 )
    {
        int i = 0;
        int j,k,ell;

        // simplify the code for the first 8 colors
        for ( ell=0 ; ell < 2 ; ell++ )
        {
            for ( k=0 ; k < 2 ; k++ )
            {
                for ( j=0 ; j < 2 ; j++ )
                {
                    Colors[i].Red = j*128;
                    Colors[i].Green = k*128;
                    Colors[i].Blue = ell*128;
                    i++;
                }
            }
        }

        // simplify the code for the last 8 colors
        for ( ell=0 ; ell < 2 ; ell++ )
        {
            for ( k=0 ; k < 2 ; k++ )
            {
                for ( j=0 ; j < 2 ; j++ )
                {
                    Colors[i].Red = j*255;
                    Colors[i].Green = k*255;
                    Colors[i].Blue = ell*255;
                    i++;
                }
            }
        }

        // overwrite the duplicate color
        i=8;
        Colors[i].Red = 192;
        Colors[i].Green = 192;
        Colors[i].Blue = 192;

        for ( i=0 ; i < 16 ; i++ )
        {
            Colors[i].Alpha = 0;
        }
        return true;
    }

    if ( BitDepth == 8 )
    {
        int i=0;
        int j,k,ell;

        // do an easy loop, which works for all but colors
        // 0 to 9 and 246 to 255
        for ( ell=0 ; ell < 4 ; ell++ )
        {
            for ( k=0 ; k < 8 ; k++ )
            {
                for ( j=0; j < 8 ; j++ )
                {
                    Colors[i].Red = j*32;
                    Colors[i].Green = k*32;
                    Colors[i].Blue = ell*64;
                    Colors[i].Alpha = 0;
                    i++;
                }
            }
        }

        // now redo the first 8 colors
        i=0;
        for ( ell=0 ; ell < 2 ; ell++ )
        {
            for ( k=0 ; k < 2 ; k++ )
            {
                for ( j=0; j < 2 ; j++ )
                {
                    Colors[i].Red = j*128;
                    Colors[i].Green = k*128;
                    Colors[i].Blue = ell*128;
                    i++;
                }
            }
        }

        // overwrite colors 7, 8, 9
        i=7;
        Colors[i].Red = 192;
        Colors[i].Green = 192;
        Colors[i].Blue = 192;
        i++; // 8
        Colors[i].Red = 192;
        Colors[i].Green = 220;
        Colors[i].Blue = 192;
        i++; // 9
        Colors[i].Red = 166;
        Colors[i].Green = 202;
        Colors[i].Blue = 240;

        // overwrite colors 246 to 255
        i=246;
        Colors[i].Red = 255;
        Colors[i].Green = 251;
        Colors[i].Blue = 240;
        i++; // 247
        Colors[i].Red = 160;
        Colors[i].Green = 160;
        Colors[i].Blue = 164;
        i++; // 248
        Colors[i].Red = 128;
        Colors[i].Green = 128;
        Colors[i].Blue = 128;
        i++; // 249
        Colors[i].Red = 255;
        Colors[i].Green = 0;
        Colors[i].Blue = 0;
        i++; // 250
        Colors[i].Red = 0;
        Colors[i].Green = 255;
        Colors[i].Blue = 0;
        i++; // 251
        Colors[i].Red = 255;
        Colors[i].Green = 255;
        Colors[i].Blue = 0;
        i++; // 252
        Colors[i].Red = 0;
        Colors[i].Green = 0;
        Colors[i].Blue = 255;
        i++; // 253
        Colors[i].Red = 255;
        Colors[i].Green = 0;
        Colors[i].Blue = 255;
        i++; // 254
        Colors[i].Red = 0;
        Colors[i].Green = 255;
        Colors[i].Blue = 255;
        i++; // 255
        Colors[i].Red = 255;
        Colors[i].Green = 255;
        Colors[i].Blue = 255;

        return true;
    }
    return true;
}

bool EBMP_SafeFread( char* buffer, int size, int number, FILE* fp )
{
    int ItemsRead;
    if ( feof(fp) )
    {
        return false;
    }
    ItemsRead = (int) fread( buffer , size , number , fp );
    if ( ItemsRead < number )
    {
        return false;
    }
    return true;
}

void BMP::BMP_SetDPI( int HorizontalDPI, int VerticalDPI )
{
    XPelsPerMeter = (int) ( HorizontalDPI * 39.37007874015748 );
    YPelsPerMeter = (int) (   VerticalDPI * 39.37007874015748 );
}

// int BMP::TellVerticalDPI( void ) const
int BMP::BMP_TellVerticalDPI( void )
{
    if ( !YPelsPerMeter )
    {
        YPelsPerMeter = DefaultYPelsPerMeter;
    }
    return (int) ( YPelsPerMeter / (double) 39.37007874015748 );
}

// int BMP::TellHorizontalDPI( void ) const
int BMP::BMP_TellHorizontalDPI( void )
{
    if ( !XPelsPerMeter )
    {
        XPelsPerMeter = DefaultXPelsPerMeter;
    }
    return (int) ( XPelsPerMeter / (double) 39.37007874015748 );
}

/* These functions are defined in EasyBMP_VariousBMPutilities.h */

void EBMP_GetBMFH(BMFH_HANDLE handle, const char* szFileNameIn )
{
    FILE* fp;
    fp = fopen( szFileNameIn,"rb");

    if ( !fp  )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Error: Cannot initialize from file ");
            printf("%s.\n", szFileNameIn);
            printf("               File cannot be opened or does not exist.\n");
        }
        handle->bfType = 0;
    }

    EBMP_SafeFread( (char*) &(handle->bfType) , sizeof(ebmpWORD) , 1 , fp );
    EBMP_SafeFread( (char*) &(handle->bfSize) , sizeof(ebmpDWORD) , 1 , fp );
    EBMP_SafeFread( (char*) &(handle->bfReserved1) , sizeof(ebmpWORD) , 1 , fp );
    EBMP_SafeFread( (char*) &(handle->bfReserved2) , sizeof(ebmpWORD) , 1 , fp );
    EBMP_SafeFread( (char*) &(handle->bfOffBits) , sizeof(ebmpDWORD) , 1 , fp );

    fclose( fp );

    if ( EBMP_IsBigEndian() )
    {
        BMFH_SwitchEndianess(handle);
    }
}

BMIH EBMP_GetBMIH( const char* szFileNameIn )
{
    BMIH bmih;

    FILE* fp;
    fp = fopen( szFileNameIn,"rb");

    if ( !fp  )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Error: Cannot initialize from file ");
            printf("%s.\n", szFileNameIn);
            printf("               File cannot be opened or does not exist.\n");
        }
        return bmih;
    }

// read the bmfh, i.e., first 14 bytes (just to get it out of the way);

    ebmpBYTE TempBYTE;
    int i;
    for ( i = 14 ; i > 0 ; i-- )
    {
        EBMP_SafeFread( (char*) &TempBYTE , sizeof(ebmpBYTE) , 1, fp );
    }

// read the bmih

    EBMP_SafeFread( (char*) &(bmih.biSize) , sizeof(ebmpDWORD) , 1 , fp );
    EBMP_SafeFread( (char*) &(bmih.biWidth) , sizeof(ebmpDWORD) , 1 , fp );
    EBMP_SafeFread( (char*) &(bmih.biHeight) , sizeof(ebmpDWORD) , 1 , fp );
    EBMP_SafeFread( (char*) &(bmih.biPlanes) , sizeof(ebmpWORD) , 1 , fp );

    EBMP_SafeFread( (char*) &(bmih.biBitCount) , sizeof(ebmpWORD) , 1 , fp );
    EBMP_SafeFread( (char*) &(bmih.biCompression) , sizeof(ebmpDWORD) , 1 , fp );
    EBMP_SafeFread( (char*) &(bmih.biSizeImage) , sizeof(ebmpDWORD) , 1 , fp );
    EBMP_SafeFread( (char*) &(bmih.biXPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp );

    EBMP_SafeFread( (char*) &(bmih.biYPelsPerMeter) , sizeof(ebmpDWORD) , 1 , fp );
    EBMP_SafeFread( (char*) &(bmih.biClrUsed) , sizeof(ebmpDWORD) , 1 , fp );
    EBMP_SafeFread( (char*) &(bmih.biClrImportant) , sizeof(ebmpDWORD) , 1 , fp );

    fclose( fp );

    if ( EBMP_IsBigEndian() )
    {
        bmih.BMIH_SwitchEndianess();
    }

    return bmih;
}

void EBMP_DisplayBitmapInfo( const char* szFileNameIn )
{
    FILE* fp;
    fp = fopen( szFileNameIn,"rb");

    if ( !fp  )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Error: Cannot initialize from file ");
            printf("%s.\n", szFileNameIn);
            printf("               File cannot be opened or does not exist.\n");
        }
        return;
    }
    fclose( fp );

// don't duplicate work! Just use the functions from above!

    BMIH bmih = EBMP_GetBMIH(szFileNameIn);

    printf("File information for file %s:\n\n", szFileNameIn);

    {
	    BMFH_HANDLE bmfh = BMFH_Construct();
	    EBMP_GetBMFH(bmfh, szFileNameIn);
	    printf("BITMAPFILEHEADER:\n");
	    printf("bfType: %u\n", (ebmpDWORD)bmfh->bfType);
	    printf("bfSize: %u\n", bmfh->bfSize);
	    printf("bfReserved1: %u\n", (ebmpDWORD)bmfh->bfReserved1);
	    printf("bfReserved2: %u\n", (ebmpDWORD)bmfh->bfReserved2);
	    printf("bfOffBits: %u\n\n", bmfh->bfOffBits);
	    BMFH_Destruct(bmfh);
    }

    printf("BITMAPINFOHEADER:\n");
    printf("biSize: %u\n", bmih.biSize);
    printf("biWidth: %u\n", bmih.biWidth);
    printf("biHeight: %u\n", bmih.biHeight);
    printf("biPlanes: %u\n", (ebmpDWORD)bmih.biPlanes);
    printf("biBitCount: %u\n", (ebmpDWORD)bmih.biBitCount);
    printf("biCompression: %u\n", bmih.biCompression);
    printf("biSizeImage: %u\n", bmih.biSizeImage);
    printf("biXPelsPerMeter: %u\n", bmih.biXPelsPerMeter);
    printf("biYPelsPerMeter: %u\n", bmih.biYPelsPerMeter);
    printf("biClrUsed: %u\n", bmih.biClrUsed);
    printf("biClrImportant: %u\n\n", bmih.biClrImportant);

    return;
}

int EBMP_GetBitmapColorDepth( const char* szFileNameIn )
{
    BMIH bmih = EBMP_GetBMIH( szFileNameIn );
    return (int) bmih.biBitCount;
}

void EBMP_PixelToPixelCopy( BMP& From, int FromX, int FromY,
                       BMP& To, int ToX, int ToY)
{
    *To(ToX,ToY) = *From(FromX,FromY);
    return;
}

void EBMP_PixelToPixelCopyTransparent( BMP& From, int FromX, int FromY,
                                  BMP& To, int ToX, int ToY,
                                  EBMP_RGBApixel& Transparent )
{
    if ( From(FromX,FromY)->Red != Transparent.Red ||
            From(FromX,FromY)->Green != Transparent.Green ||
            From(FromX,FromY)->Blue != Transparent.Blue )
    {
        *To(ToX,ToY) = *From(FromX,FromY);
    }
    return;
}

void EBMP_RangedPixelToPixelCopy( BMP& From, int FromL , int FromR, int FromB, int FromT,
                             BMP& To, int ToX, int ToY )
{
// make sure the conventions are followed
    if ( FromB < FromT )
    {
        int Temp = FromT;
        FromT = FromB;
        FromB = Temp;
    }

// make sure that the copied regions exist in both bitmaps
    if ( FromR >= From.BMP_TellWidth() )
    {
        FromR = From.BMP_TellWidth()-1;
    }
    if ( FromL < 0 )
    {
        FromL = 0;
    }

    if ( FromB >= From.BMP_TellHeight() )
    {
        FromB = From.BMP_TellHeight()-1;
    }
    if ( FromT < 0 )
    {
        FromT = 0;
    }

    if ( ToX+(FromR-FromL) >= To.BMP_TellWidth() )
    {
        FromR = To.BMP_TellWidth()-1+FromL-ToX;
    }
    if ( ToY+(FromB-FromT) >= To.BMP_TellHeight() )
    {
        FromB = To.BMP_TellHeight()-1+FromT-ToY;
    }

    int i,j;
    for ( j=FromT ; j <= FromB ; j++ )
    {
        for ( i=FromL ; i <= FromR ; i++ )
        {
            EBMP_PixelToPixelCopy( From, i,j,
                              To, ToX+(i-FromL), ToY+(j-FromT) );
        }
    }

    return;
}

void EBMP_RangedPixelToPixelCopyTransparent(
    BMP& From, int FromL , int FromR, int FromB, int FromT,
    BMP& To, int ToX, int ToY ,
    EBMP_RGBApixel& Transparent )
{
// make sure the conventions are followed
    if ( FromB < FromT )
    {
        int Temp = FromT;
        FromT = FromB;
        FromB = Temp;
    }

// make sure that the copied regions exist in both bitmaps
    if ( FromR >= From.BMP_TellWidth() )
    {
        FromR = From.BMP_TellWidth()-1;
    }
    if ( FromL < 0 )
    {
        FromL = 0;
    }

    if ( FromB >= From.BMP_TellHeight() )
    {
        FromB = From.BMP_TellHeight()-1;
    }
    if ( FromT < 0 )
    {
        FromT = 0;
    }

    if ( ToX+(FromR-FromL) >= To.BMP_TellWidth() )
    {
        FromR = To.BMP_TellWidth()-1+FromL-ToX;
    }
    if ( ToY+(FromB-FromT) >= To.BMP_TellHeight() )
    {
        FromB = To.BMP_TellHeight()-1+FromT-ToY;
    }

    int i,j;
    for ( j=FromT ; j <= FromB ; j++ )
    {
        for ( i=FromL ; i <= FromR ; i++ )
        {
            EBMP_PixelToPixelCopyTransparent( From, i,j,
                                         To, ToX+(i-FromL), ToY+(j-FromT) ,
                                         Transparent);
        }
    }

    return;
}

bool EBMP_CreateGrayscaleColorTable( BMP& InputImage )
{
    int BitDepth = InputImage.BMP_TellBitDepth();
    if ( BitDepth != 1 && BitDepth != 4 && BitDepth != 8 )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Warning: Attempted to create color table at a bit\n");
            printf("                 depth that does not require a color table.\n");
            printf("                 Ignoring request.\n");
        }
        return false;
    }
    int i;
    int NumberOfColors = InputImage.BMP_TellNumberOfColors();

    ebmpBYTE StepSize;
    if ( BitDepth != 1 )
    {
        StepSize = 255/(NumberOfColors-1);
    }
    else
    {
        StepSize = 255;
    }

    for ( i=0 ; i < NumberOfColors ; i++ )
    {
        ebmpBYTE TempBYTE = i*StepSize;
        EBMP_RGBApixel TempColor;
        TempColor.Red = TempBYTE;
        TempColor.Green = TempBYTE;
        TempColor.Blue = TempBYTE;
        TempColor.Alpha = 0;
        InputImage.BMP_SetColor( i , TempColor );
    }
    return true;
}

bool BMP::BMP_Read32bitRow( ebmpBYTE* Buffer, int BufferSize, int Row )
{
    int i;
    if ( Width*4 > BufferSize )
    {
        return false;
    }
    for ( i=0 ; i < Width ; i++ )
    {
        memcpy( (char*) &(Pixels[i][Row]), (char*) Buffer+4*i, 4 );
    }
    return true;
}

bool BMP::BMP_Read24bitRow( ebmpBYTE* Buffer, int BufferSize, int Row )
{
    int i;
    if ( Width*3 > BufferSize )
    {
        return false;
    }
    for ( i=0 ; i < Width ; i++ )
    {
        memcpy( (char*) &(Pixels[i][Row]), Buffer+3*i, 3 );
    }
    return true;
}

bool BMP::BMP_Read8bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
{
    int i;
    if ( Width > BufferSize )
    {
        return false;
    }
    for ( i=0 ; i < Width ; i++ )
    {
        int Index = Buffer[i];
        *( this->operator()(i,Row) )= BMP_GetColor(Index);
    }
    return true;
}

bool BMP::BMP_Read4bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
{
    int Shifts[2] = {4  ,0 };
    int Masks[2]  = {240,15};

    int i=0;
    int j;
    int k=0;
    if ( Width > 2*BufferSize )
    {
        return false;
    }
    while ( i < Width )
    {
        j=0;
        while ( j < 2 && i < Width )
        {
            int Index = (int) ( (Buffer[k]&Masks[j]) >> Shifts[j]);
            *( this->operator()(i,Row) )= BMP_GetColor(Index);
            i++;
            j++;
        }
        k++;
    }
    return true;
}
bool BMP::BMP_Read1bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
{
    int Shifts[8] = {7  ,6 ,5 ,4 ,3,2,1,0};
    int Masks[8]  = {128,64,32,16,8,4,2,1};

    int i=0;
    int j;
    int k=0;

    if ( Width > 8*BufferSize )
    {
        return false;
    }
    while ( i < Width )
    {
        j=0;
        while ( j < 8 && i < Width )
        {
            int Index = (int) ( (Buffer[k]&Masks[j]) >> Shifts[j]);
            *( this->operator()(i,Row) )= BMP_GetColor(Index);
            i++;
            j++;
        }
        k++;
    }
    return true;
}

bool BMP::BMP_Write32bitRow( ebmpBYTE* Buffer, int BufferSize, int Row )
{
    int i;
    if ( Width*4 > BufferSize )
    {
        return false;
    }
    for ( i=0 ; i < Width ; i++ )
    {
        memcpy( (char*) Buffer+4*i, (char*) &(Pixels[i][Row]), 4 );
    }
    return true;
}

bool BMP::BMP_Write24bitRow( ebmpBYTE* Buffer, int BufferSize, int Row )
{
    int i;
    if ( Width*3 > BufferSize )
    {
        return false;
    }
    for ( i=0 ; i < Width ; i++ )
    {
        memcpy( (char*) Buffer+3*i,  (char*) &(Pixels[i][Row]), 3 );
    }
    return true;
}

bool BMP::BMP_Write8bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
{
    int i;
    if ( Width > BufferSize )
    {
        return false;
    }
    for ( i=0 ; i < Width ; i++ )
    {
        Buffer[i] = BMP_FindClosestColor( Pixels[i][Row] );
    }
    return true;
}

bool BMP::BMP_Write4bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
{
    int PositionWeights[2]  = {16,1};

    int i=0;
    int j;
    int k=0;
    if ( Width > 2*BufferSize )
    {
        return false;
    }
    while ( i < Width )
    {
        j=0;
        int Index = 0;
        while ( j < 2 && i < Width )
        {
            Index += ( PositionWeights[j]* (int) BMP_FindClosestColor( Pixels[i][Row] ) );
            i++;
            j++;
        }
        Buffer[k] = (ebmpBYTE) Index;
        k++;
    }
    return true;
}

bool BMP::BMP_Write1bitRow(  ebmpBYTE* Buffer, int BufferSize, int Row )
{
    int PositionWeights[8]  = {128,64,32,16,8,4,2,1};

    int i=0;
    int j;
    int k=0;
    if ( Width > 8*BufferSize )
    {
        return false;
    }
    while ( i < Width )
    {
        j=0;
        int Index = 0;
        while ( j < 8 && i < Width )
        {
            Index += ( PositionWeights[j]* (int) BMP_FindClosestColor( Pixels[i][Row] ) );
            i++;
            j++;
        }
        Buffer[k] = (ebmpBYTE) Index;
        k++;
    }
    return true;
}

ebmpBYTE BMP::BMP_FindClosestColor( EBMP_RGBApixel& input )
{
    int i=0;
    int NumberOfColors = BMP_TellNumberOfColors();
    ebmpBYTE BestI = 0;
    int BestMatch = 999999;

    while ( i < NumberOfColors )
    {
        EBMP_RGBApixel Attempt = BMP_GetColor( i );
        int TempMatch = EBMP_IntSquare( (int) Attempt.Red - (int) input.Red )
                        + EBMP_IntSquare( (int) Attempt.Green - (int) input.Green )
                        + EBMP_IntSquare( (int) Attempt.Blue - (int) input.Blue );
        if ( TempMatch < BestMatch )
        {
            BestI = (ebmpBYTE) i;
            BestMatch = TempMatch;
        }
        if ( BestMatch < 1 )
        {
            i = NumberOfColors;
        }
        i++;
    }
    return BestI;
}

bool EBMP_EasyBMPcheckDataSize( void )
{
    bool ReturnValue = true;
    if ( sizeof( ebmpBYTE ) != 1 )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Error: ebmpBYTE has the wrong size (");
            printf("%u bytes),\n", (ebmpDWORD)sizeof( ebmpBYTE ));
            printf("               Compared to the expected 1 byte value\n");
        }
        ReturnValue = false;
    }
    if ( sizeof( ebmpWORD ) != 2 )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Error: ebmpWORD has the wrong size (");
            printf("%u bytes),\n", (ebmpDWORD)sizeof( ebmpWORD ));
            printf("               Compared to the expected 2 byte value\n");
        }
        ReturnValue = false;
    }
    if ( sizeof( ebmpDWORD ) != 4 )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Error: ebmpDWORD has the wrong size (");
            printf("%u bytes),\n", (ebmpDWORD)sizeof( ebmpDWORD ));
            printf("               Compared to the expected 4 byte value\n");
        }
        ReturnValue = false;
    }
    return ReturnValue;
}

bool EBMP_Rescale( BMP& InputImage , char mode, int NewDimension )
{
    int CapMode = toupper( mode );

    BMP OldImage( InputImage );

    if ( CapMode != 'P' &&
            CapMode != 'W' &&
            CapMode != 'H' &&
            CapMode != 'F' )
    {
        if ( EBMP_EasyBMPwarnings )
        {
            printf("EasyBMP Error: Unknown rescale mode %c requested\n", mode);
        }
        return false;
    }

    int NewWidth  =0;
    int NewHeight =0;

    int OldWidth = OldImage.BMP_TellWidth();
    int OldHeight= OldImage.BMP_TellHeight();

    if ( CapMode == 'P' )
    {
        NewWidth = (int) floor( OldWidth * NewDimension / 100.0 );
        NewHeight = (int) floor( OldHeight * NewDimension / 100.0 );
    }
    if ( CapMode == 'F' )
    {
        if ( OldWidth > OldHeight )
        {
            CapMode = 'W';
        }
        else
        {
            CapMode = 'H';
        }
    }

    if ( CapMode == 'W' )
    {
        double percent = (double) NewDimension / (double) OldWidth;
        NewWidth = NewDimension;
        NewHeight = (int) floor( OldHeight * percent );
    }
    if ( CapMode == 'H' )
    {
        double percent = (double) NewDimension / (double) OldHeight;
        NewHeight = NewDimension;
        NewWidth = (int) floor( OldWidth * percent );
    }

    if ( NewWidth < 1 )
    {
        NewWidth = 1;
    }
    if ( NewHeight < 1 )
    {
        NewHeight = 1;
    }

    InputImage.BMP_SetSize( NewWidth, NewHeight );
    InputImage.BMP_SetBitDepth( 24 );

    int I,J;
    double ThetaI,ThetaJ;

    for ( int j=0; j < NewHeight-1 ; j++ )
    {
        ThetaJ = (double)(j*(OldHeight-1.0))
                 /(double)(NewHeight-1.0);
        J	= (int) floor( ThetaJ );
        ThetaJ -= J;

        for ( int i=0; i < NewWidth-1 ; i++ )
        {
            ThetaI = (double)(i*(OldWidth-1.0))
                     /(double)(NewWidth-1.0);
            I = (int) floor( ThetaI );
            ThetaI -= I;

            InputImage(i,j)->Red = (ebmpBYTE)
                                   ( (1.0-ThetaI-ThetaJ+ThetaI*ThetaJ)*(OldImage(I,J)->Red)
                                     +(ThetaI-ThetaI*ThetaJ)*(OldImage(I+1,J)->Red)
                                     +(ThetaJ-ThetaI*ThetaJ)*(OldImage(I,J+1)->Red)
                                     +(ThetaI*ThetaJ)*(OldImage(I+1,J+1)->Red) );
            InputImage(i,j)->Green = (ebmpBYTE)
                                     ( (1.0-ThetaI-ThetaJ+ThetaI*ThetaJ)*OldImage(I,J)->Green
                                       +(ThetaI-ThetaI*ThetaJ)*OldImage(I+1,J)->Green
                                       +(ThetaJ-ThetaI*ThetaJ)*OldImage(I,J+1)->Green
                                       +(ThetaI*ThetaJ)*OldImage(I+1,J+1)->Green );
            InputImage(i,j)->Blue = (ebmpBYTE)
                                    ( (1.0-ThetaI-ThetaJ+ThetaI*ThetaJ)*OldImage(I,J)->Blue
                                      +(ThetaI-ThetaI*ThetaJ)*OldImage(I+1,J)->Blue
                                      +(ThetaJ-ThetaI*ThetaJ)*OldImage(I,J+1)->Blue
                                      +(ThetaI*ThetaJ)*OldImage(I+1,J+1)->Blue );
        }
        InputImage(NewWidth-1,j)->Red = (ebmpBYTE)
                                        ( (1.0-ThetaJ)*(OldImage(OldWidth-1,J)->Red)
                                          + ThetaJ*(OldImage(OldWidth-1,J+1)->Red) );
        InputImage(NewWidth-1,j)->Green = (ebmpBYTE)
                                          ( (1.0-ThetaJ)*(OldImage(OldWidth-1,J)->Green)
                                            + ThetaJ*(OldImage(OldWidth-1,J+1)->Green) );
        InputImage(NewWidth-1,j)->Blue = (ebmpBYTE)
                                         ( (1.0-ThetaJ)*(OldImage(OldWidth-1,J)->Blue)
                                           + ThetaJ*(OldImage(OldWidth-1,J+1)->Blue) );
    }

    for ( int i=0 ; i < NewWidth-1 ; i++ )
    {
        ThetaI = (double)(i*(OldWidth-1.0))
                 /(double)(NewWidth-1.0);
        I = (int) floor( ThetaI );
        ThetaI -= I;
        InputImage(i,NewHeight-1)->Red = (ebmpBYTE)
                                         ( (1.0-ThetaI)*(OldImage(I,OldHeight-1)->Red)
                                           + ThetaI*(OldImage(I,OldHeight-1)->Red) );
        InputImage(i,NewHeight-1)->Green = (ebmpBYTE)
                                           ( (1.0-ThetaI)*(OldImage(I,OldHeight-1)->Green)
                                             + ThetaI*(OldImage(I,OldHeight-1)->Green) );
        InputImage(i,NewHeight-1)->Blue = (ebmpBYTE)
                                          ( (1.0-ThetaI)*(OldImage(I,OldHeight-1)->Blue)
                                            + ThetaI*(OldImage(I,OldHeight-1)->Blue) );
    }

    *InputImage(NewWidth-1,NewHeight-1) = *OldImage(OldWidth-1,OldHeight-1);
    return true;
}
